/*
 * Copyright (C) 2012 ProFUSION embedded systems. All rights reserved.
 * Copyright (C) 2012 Samsung Electronics
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "RunLoop.h"

#include <core/looper.h>
#include <chrono>
#include <map>
#include <core/looper.h>
#include <core/systemclock.h>
#include <limits.h>
#include <sys/timerfd.h>
#include <unistd.h>
#include <time.h>

//#define USE_TIMERFD 1

namespace WTF {

#ifndef USE_TIMERFD
class TimerScheduler:public cdroid::EventHandler{
private:
    RunLoop::TimerBase*m_Timer;
public:
    TimerScheduler(RunLoop::TimerBase*timer){
        m_Timer=timer;
    }

    int checkEvents()override{
        auto now = cdroid::SystemClock::uptimeMillis();
        return now>m_Timer->m_nextTime;
    }

    int handleEvents()override{
	auto now = cdroid::SystemClock::uptimeMillis();
        m_Timer->fired();
	if(m_Timer->m_isRepeating)
	    m_Timer->m_nextTime=now+m_Timer->m_interval;
        return 0;
    }
};
#endif

RunLoop::RunLoop(){
    printf("==================new RunLoop %p=======\r\n",this);
}

RunLoop::~RunLoop(){
}

void RunLoop::run(){
    cdroid::Looper::getDefault()->pollAll(2);
}

void RunLoop::stop(){
    //ecore_main_loop_quit();
}

void RunLoop::wakeUp(){
    cdroid::Looper::getDefault()->wake();
}

int RunLoop::createTimerFd(){
    int fd;
    if(m_timers.size()){
        fd=m_timers.first();
        m_timers.removeFirst();
    }
    fd=timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
    return fd;
}

int RunLoop::recycleTimerFd(int fd){
    if(fd<0)return -1;
    m_timers.append(fd);
    return m_timers.size();
}

RunLoop::TimerBase::TimerBase(RunLoop& runLoop)
    : m_runLoop(runLoop),m_interval(INT_MAX),
      m_isRepeating(false){
    m_Handler=nullptr; 
}

RunLoop::TimerBase::~TimerBase(){
    stop();
}

static void ms2timespec(int ms, struct timespec *ts){
    ts->tv_sec = ms / 1000;
    ts->tv_nsec = (ms % 1000) * 1000000;
}

static int timerFDCBK(int fd, int events, void* data){
    uint64_t count;
    RunLoop::TimerBase*thiz=(RunLoop::TimerBase*)data;
    if(events&cdroid::Looper::EVENT_INPUT)
        ::read(fd, &count, sizeof(uint64_t));
    ((RunLoop::TimerBase*)data)->fired();
    printf("%d is fired\r\n",fd);
}

void RunLoop::TimerBase::start(double nextFireInterval, bool repeat){
    if (isActive())
        stop();
#ifndef USE_TIMERFD    
    m_nextTime=cdroid::SystemClock::uptimeMillis()+nextFireInterval*1000;
    m_isRepeating = repeat;
    m_interval= nextFireInterval*1000;
    m_Handler=new TimerScheduler(this);
    cdroid::Looper::getDefault()->addEventHandler(m_Handler);
#else
    struct itimerspec new_value={{0,0},{0,0}};
    int fd=m_runLoop.createTimerFd();
    nextFireInterval=std::max(nextFireInterval,0.001);
    ms2timespec(nextFireInterval*1000,&new_value.it_value);
    if(repeat)
	ms2timespec(nextFireInterval*1000,&new_value.it_interval);
    timerfd_settime(fd, 0, &new_value, NULL);
    cdroid::Looper::getDefault()->addFd(fd,0,cdroid::Looper::EVENT_INPUT,timerFDCBK,this);
    m_Handler=(cdroid::EventHandler*)fd;
    printf("%p nextFireInterval=%f fd=%d repeat=%d\r\n",this,nextFireInterval,fd,repeat);
#endif    
}

void RunLoop::TimerBase::stop(){
#ifndef  USE_TIMERFD	
     cdroid::Looper::getDefault()->removeEventHandler(m_Handler);
     m_Handler=nullptr;
#else
     int fd=(int)m_Handler;
     struct itimerspec new_value={{0,0},{0,0}};
     timerfd_settime(fd, 0, &new_value, NULL);
     cdroid::Looper::getDefault()->removeFd(fd);
     m_runLoop.recycleTimerFd(fd);
     m_Handler=nullptr;
#endif
}

bool RunLoop::TimerBase::isActive() const{
    return m_Handler!=nullptr;
}

} // namespace WTF
